package com.zcxy.util;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author gxp
 * @create 2020/5/19 15:12
 * @Ver 1.0.0
 */
public class BeanCopierUtil {
    /**
     * BeanCopier的缓存
     */
    static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIER_CACHE = new ConcurrentHashMap<>();

    /**
     * BeanCopier的copy
     *
     * source 源文件的
     * target 目标文件
     *               的作用是将 setter 方法的返回值由 void 修改为 this。
     *               这导致 setter 的方法签名改变，最终导致 BeanCopier 无法识别现有的 setter 方法。
     */
    public static <S, T> T copyObject(S source, T target, BeanCopierUtilCallBack beanCopierUtilCallBack) {
        if (target == null) {
            return null;
        }
        BeanCopierUtil.copy(source, target, true);
        if (beanCopierUtilCallBack != null) {
            beanCopierUtilCallBack.callBack(source, target);
        }
        return target;
    }

    public static <S, T> T copyObject(S source, T target) {
        return BeanCopierUtil.copyObject(source, target, null);
    }


    public static <S, T> T copyObject(S source, Class<T> target, BeanCopierUtilCallBack<S, T> beanCopierUtilCallBack) {
        T t = null;
        try {
            t = target.newInstance();
            BeanCopierUtil.copy(source, t, true);
            if (beanCopierUtilCallBack != null) {
                beanCopierUtilCallBack.callBack(source, t);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        if (beanCopierUtilCallBack != null) {
            beanCopierUtilCallBack.callBack(source, t);
        }
        return t;
    }

    public static <S, T> IPage<T> copyPage(IPage<S> source, Class<T> target) {
        IPage<T> tiPage = new Page<>();
        BeanCopierUtil.copyObject(source, tiPage);
        tiPage.setTotal(source.getTotal());
        tiPage.setRecords(BeanCopierUtil.copyListToList(source.getRecords(), target));
        return tiPage;
    }

    public static <S, T> T copyObject(S source, Class<T> target) {
        return copyObject(source, target, null);
    }

    public static <S, T> List<T> copyListToList(List<S> source, Class<T> target, BeanCopierUtilCallBack<S, T> beanCopierUtilCallBack) {
        if (CollectionUtil.isEmpty(source)) {
            return null;
        }
        List<T> ts = new ArrayList<>();
        source.stream().forEach(i -> {
            try {
                T t = target.newInstance();
                BeanCopierUtil.copy(i, t, true);
                if (beanCopierUtilCallBack != null) {
                    beanCopierUtilCallBack.callBack(i, t);
                }
                ts.add(t);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return ts;
    }

    public static <S, T> List<T> copyListToList(List<S> source, Class<T> target) {
        return BeanCopierUtil.copyListToList(source, target, null);
    }

    public static <S, T> Set<T> copyListToSet(List<S> source, Class<T> target) {
        if (CollectionUtil.isEmpty(source)) {
            return null;
        }
        Set<T> ts = new HashSet<>();
        source.stream().forEach(i -> {
            try {
                T t = target.newInstance();
                BeanCopierUtil.copy(i, t, true);
                ts.add(t);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return ts;
    }


    /**
     * 生成key
     *
     * srcClazz 源文件的class
     * tgtClazz 目标文件的class
     * @return string
     */
    private static String genKey(Class<?> srcClazz, Class<?> tgtClazz) {
        return srcClazz.getName() + tgtClazz.getName();
    }


    private static String getPropertyName(String methodName) {//setAge ---> age
        char[] newChar = new char[methodName.length() - 3];
        System.arraycopy(methodName.toCharArray(), 3, newChar, 0, methodName.length() - 3);
        newChar[0] = Character.toLowerCase(newChar[0]);
        return String.valueOf(newChar);
    }


    private static BeanCopier getBeanCopier(Class<?> sourceClass, Class<?> targetClass) {
        String beanKey = genKey(sourceClass, targetClass);
        BeanCopier copier = null;
        if (!BEAN_COPIER_CACHE.containsKey(beanKey)) {
            copier = BeanCopier.create(sourceClass, targetClass, true);
            BEAN_COPIER_CACHE.put(beanKey, copier);
        } else {
            copier = BEAN_COPIER_CACHE.get(beanKey);
        }
        return copier;
    }

    /**
     * true 代表copy 忽略null 值
     *
     * source
     * target
     * cover
     */
    public static void copy(Object source, Object target, boolean cover) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
        copier.copy(source, target, (value, t, context) -> {
            if (cover && value == null) {
                return BeanMap.create(target).get(getPropertyName(String.valueOf(context)));
            }
            return value;
        });
    }


}
